package ch.ethz.fcl.metrobuzz.spatialindex.RTree;

import ch.ethz.fcl.metrobuzz.spatialindex.object3d.BoundingBox;

public class RTreeNode {
	int nodeId = 0;
	BoundingBox bb = new BoundingBox(Float.MAX_VALUE, Float.MAX_VALUE,
			Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE,
			-Float.MAX_VALUE);
	
	BoundingBox[] entries;

	int[] ids = null;
	int level;
	int entryCount;

	RTreeNode(int nodeId, int level, int maxNodeEntries) {
		this.nodeId = nodeId;
		this.level = level;
		entries = new BoundingBox[maxNodeEntries];
		ids = new int[maxNodeEntries];
	}

	void addEntry(BoundingBox object, int id) {
		ids[entryCount] = id;
		entries[entryCount] = object;
		this.bb.setMinX(Math.min(object.getBoundingBox().getMinX(),
				this.bb.getMinX()));
		this.bb.setMinY(Math.min(object.getBoundingBox().getMinY(),
				this.bb.getMinY()));
		this.bb.setMinZ(Math.min(object.getBoundingBox().getMinZ(),
				this.bb.getMinZ()));
		this.bb.setMaxX(Math.max(object.getBoundingBox().getMaxX(),
				this.bb.getMaxX()));
		this.bb.setMaxY(Math.max(object.getBoundingBox().getMaxY(),
				this.bb.getMaxY()));
		this.bb.setMaxZ(Math.max(object.getBoundingBox().getMaxZ(),
				this.bb.getMaxZ()));
		entryCount++;
	}

	int findEntry(BoundingBox object, int id) {
		for (int i = 0; i < entryCount; i++) {
			if (id == ids[i] && entries[i].equals(object))
				return i;
		}
		return -1;
	}

	// delete entry. This is done by setting it to null and copying the last
	// entry into its space.
	void deleteEntry(int i) {
		int lastIndex = entryCount - 1;
		BoundingBox deletedBB = new BoundingBox();
		deletedBB.copy(entries[i].getBoundingBox());

		if (i != lastIndex) {
			entries[i] = entries[lastIndex];
			ids[i] = ids[lastIndex];
		}
		entryCount--;

		// adjust the MBB
		recalculateMBBIfInfluencedBy(deletedBB);
	}

	void recalculateMBBIfInfluencedBy(BoundingBox deletedBB) {
		if (bb.getMinX() == deletedBB.getMinX()
				|| bb.getMinY() == deletedBB.getMinY()
				|| bb.getMinZ() == deletedBB.getMinZ()
				|| bb.getMaxX() == deletedBB.getMaxX()
				|| bb.getMaxY() == deletedBB.getMaxY()
				|| bb.getMaxZ() == deletedBB.getMaxZ())
			recalculateMBB();
	}

	void recalculateMBB() {
		bb.copy(entries[0].getBoundingBox());

		for (int i = 1; i < entryCount; i++) {
			bb.setMinX(Math.min(entries[i].getBoundingBox().getMinX(),
					bb.getMinX()));
			bb.setMinY(Math.min(entries[i].getBoundingBox().getMinY(),
					bb.getMinY()));
			bb.setMinZ(Math.min(entries[i].getBoundingBox().getMinZ(),
					bb.getMinZ()));
			bb.setMaxX(Math.max(entries[i].getBoundingBox().getMaxX(),
					bb.getMinX()));
			bb.setMaxY(Math.max(entries[i].getBoundingBox().getMaxY(),
					bb.getMaxY()));
			bb.setMaxZ(Math.max(entries[i].getBoundingBox().getMaxZ(),
					bb.getMaxZ()));
		}
	}

	/**
	 * eliminate null entries, move all entries to the start of the source node
	 */
	void reorganize(RTreeSpatialIndex rtree) {
		int countdownIndex = rtree.maxNodeEntries - 1;
		for (int index = 0; index < entryCount; index++) {
			if (ids[index] == -1) {
				while (ids[countdownIndex] == -1 && countdownIndex > index) {
					countdownIndex--;
				}
				entries[index] = entries[countdownIndex];
				ids[index] = ids[countdownIndex];
				ids[countdownIndex] = -1;
			}
		}
	}

	public int getEntryCount() {
		return entryCount;
	}

	public int getId(int index) {
		if (index < entryCount) {
			return ids[index];
		}
		return -1;
	}

	boolean isLeaf() {
		return (level == 1);
	}

	public int getLevel() {
		return level;
	}

	public BoundingBox getBoundingBox() {
		return bb;
	}
}
